//
// Created by yricky on 21-4-5.
//


#include "mm.h"

int64_t YMM_ERRNO = 0;
char *YMM_addr = 0;
unsigned int YMM_size = 0;

YMM_map *YMM_fp = 0;//this->m_addr=YMM_addr
YMM_map *YMM_cp = 0;

inline YMM_map *new_map() {
    YMM_map *m = (YMM_map *) malloc(sizeof(YMM_map));
    m->nxt = 0;
    m->pre = 0;
    return m;
}
void remove_map(YMM_map *victim) {
    if (victim->pre != 0)
        victim->pre->nxt = victim->nxt;
    if (victim->nxt != 0)
        victim->nxt->pre = victim->pre;
    free(victim);
}


void YMM_init(unsigned int size) {
    YMM_addr = (char *) malloc(size);
    YMM_size = size;
    YMM_fp = new_map();
    YMM_cp = YMM_fp;

    YMM_fp->m_addr = YMM_addr;
    YMM_fp->m_size = size;
    YMM_fp->nxt = 0;
    YMM_fp->pre = 0;
}
void YMM_release() {
    if (YMM_size == 0) {
        return;
    }

    YMM_cp = YMM_fp;
    while (YMM_cp->nxt != 0) {
        YMM_cp = YMM_cp->nxt;
        free(YMM_cp->pre);
    }
    free(YMM_cp);
    YMM_fp = 0;
    YMM_cp = 0;

    free(YMM_addr);
    YMM_size = 0;
}
//lmalloc
char *YMM_alloc(unsigned int size) {
    void *tmp = YMM_cp;
    while (YMM_cp->m_size < size) {
        YMM_cp = YMM_cp->nxt == 0 ? YMM_fp : YMM_cp->nxt;
        if (YMM_cp == tmp) {
            return (char *) (0 & (YMM_ERRNO = YMMERR_NO_ENOUGH_SPACE));
        }
    }
    YMM_cp->m_size -= size;
    tmp = YMM_cp->m_addr + YMM_cp->m_size;
    if (YMM_cp->m_size == 0 && YMM_cp != YMM_fp) {
        YMM_cp = YMM_cp->pre;
        remove_map(YMM_cp->nxt);
    } else {
        YMM_cp = YMM_cp->nxt ? YMM_cp->nxt : YMM_fp;
    }
    return (char *) tmp;
}
//lfree
void YMM_free(unsigned int size, char *addr) {
    YMM_map *bp = YMM_fp;
    char *maddr;
    unsigned int msize;
    while (bp != 0) {
        maddr = bp->m_addr + bp->m_size;
        msize = bp->nxt == 0 ? YMM_addr + YMM_size - maddr : bp->nxt->m_addr - maddr;
        if (addr > maddr && addr + size < maddr + msize) {
            YMM_map *tmp = new_map();
            tmp->m_addr = addr;
            tmp->m_size = size;
            tmp->pre = bp;
            tmp->nxt = bp->nxt;
            if (bp->nxt != 0)
                bp->nxt->pre = tmp;
            bp->nxt = tmp;
            return;
        } else if (addr == maddr && size < msize) {
            YMM_cp = bp;
            bp->m_size += size;
            return;
        } else if (addr > maddr && addr + size == maddr + msize) {
            if (bp->nxt == 0) {
                bp->nxt = new_map();
                bp->nxt->pre = bp;
                bp->nxt->m_addr = addr;
                bp->nxt->m_size = size;
            } else {
                bp->nxt->m_addr -= size;
            }
            return;
        } else if (addr == maddr && size == msize) {
            if (bp->nxt == 0) {
                bp->m_size += size;
            } else {
                bp->m_size += size + bp->nxt->m_size;
                remove_map(bp->nxt);
            }
            YMM_cp = bp;
            return;
        }
        bp = bp->nxt;
    }
}
